///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_DBUS_CBUS_H_
#define _BUOLA_DBUS_CBUS_H_

#include <buola/dbus.h>

#include <buola/utility/usignal.h>
#include <buola/io/cfdlistener.h>
#include <buola/app/ceventloop.h>
#include <unordered_map>

namespace buola { namespace dbus {

///\addtogroup dbus
///@{

/////////////////////////////////////////////////////////////////////////

/// D-BUS connection

/// this class represents a connection to a message bus.
/// it also has static functions to obtain the default buses.
///
/////////////////////////////////////////////////////////////////////////

class CBus : public UUncopiable,public io::CFDListener,public CLoopFunction
{
private:
    typedef USignal<void(CMessage*)> tSignal;
    
    //! flags used when registering a service
    enum ERegisterFlags
    {
        REGISTER_NONE=0,
        REGISTER_ALLOW_REPLACEMENT=(1<<0),  //!< the service can be replaced
        REGISTER_REPLACE_EXISTING=(1<<1)    //!< replace an existing service with the same name
    };

    DECLARE_FRIEND_ENUM_BITOPS(ERegisterFlags)

    //! implementation details
    struct SPrivate;

//! \name constructor and destructor
//@{
    CBus(EBusType pType);
    virtual ~CBus();
//@}

//! \name internal methods
//@{
    //!actually makes the connection
    void Connect();
//@}

//! \name internal interface
//! to be used from other parts within the library
//@{
    void AddObject(CObjectBase*);
    void RemoveObject(CObjectBase*);
    void AddObjectTree(CObjectBase*);
    void RemoveObjectTree(CObjectBase*);

    std::string GetObjectMatchRule(const std::string &pPath);
    std::string GetInterfaceMatchRule(const std::string &pPath,const std::string &pInterface);
    std::string GetSignalMatchRule(const std::string &pPath,const std::string &pInterface,const std::string &pSignal);
    
    tSignal *RegisterObjectMatch(const std::string &pPath);
    tSignal *RegisterInterfaceMatch(const std::string &pPath,const std::string &pInterface);
//@}

public:
//! \name public interface
//@{
    //! \brief registers a D-BUS service
    //!
    //! \param pService address of the service (ex. org.buola.BConf)
    //! \param pFlags flags to use when registering the service
    bool RegisterService(const std::string &pService,ERegisterFlags pFlags=REGISTER_NONE);
//@}

private:
    virtual void OnFDListener(int,io::ENotify,void*);
    virtual bool OnLoopFunction();

    CObjectBase *GetTarget(CMessage *pM);

public:
//! \name callback mechanism
//! to connect with the dbus library
//@{
    bool ProcessMethodCall(CMessage *pM);
    bool ProcessSignal(CMessage *pM);
    CMessage *GetQueuedReply(int pSerial);

    SPrivate *GetPrivate()  {   return mP;  }
    
    uint32_t SendMessage(CMessage *pM);
    PMessage SendMessageEnqueue(CMessage *pM);
    PMessage SendMessageWait(CMessage *pM);
//@}

private:
    std::map<std::string,tSignal*> mObjectMatches;
    std::map<std::string,tSignal*> mInterfaceMatches;
    std::vector<CObjectBase*> mObjects;
    std::vector<CObjectBase*> mObjectTrees;
    std::unordered_map<int,CMessage*> mPendingReplies;

    SPrivate *mP;
    EBusType mType;

    friend CBus *get_bus(EBusType);    
    friend CBus *get_session_bus();
    friend CBus *get_system_bus();
    
    template<typename tType>
    friend class CObject;
    friend class CObjectBase;
    friend class CProxy;
};

///@}

/*namespace dbus*/ } /*namespace buola*/ }

#endif
